home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 151 / cd-rom 151.iso / internet / firefox / Firefox Setup 3.0 Beta 1.exe / nonlocalized / components / nsUrlClassifierLib.js < prev    next >
Encoding:
Text File  |  2007-11-09  |  59.5 KB  |  1,855 lines

  1. //@line 37 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\src\nsUrlClassifierLib.js"
  2.  
  3. // We wastefully reload the same JS files across components.  This puts all
  4. // the common JS files used by safebrowsing and url-classifier into a
  5. // single component.
  6.  
  7. const Cc = Components.classes;
  8. const Ci = Components.interfaces;
  9. const G_GDEBUG = false;
  10.  
  11. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\lang.js"
  12.  
  13.  
  14. /**
  15.  * lang.js - Some missing JavaScript language features
  16.  */
  17.  
  18. /**
  19.  * Partially applies a function to a particular "this object" and zero or
  20.  * more arguments. The result is a new function with some arguments of the first
  21.  * function pre-filled and the value of |this| "pre-specified".
  22.  *
  23.  * Remaining arguments specified at call-time are appended to the pre-
  24.  * specified ones.
  25.  *
  26.  * Usage:
  27.  * var barMethBound = BindToObject(myFunction, myObj, "arg1", "arg2");
  28.  * barMethBound("arg3", "arg4");
  29.  *
  30.  * @param fn {string} Reference to the function to be bound
  31.  *
  32.  * @param self {object} Specifies the object which |this| should point to
  33.  * when the function is run. If the value is null or undefined, it will default
  34.  * to the global object.
  35.  *
  36.  * @returns {function} A partially-applied form of the speficied function.
  37.  */
  38. function BindToObject(fn, self, opt_args) {
  39.   var boundargs = fn.boundArgs_ || [];
  40.   boundargs = boundargs.concat(Array.slice(arguments, 2, arguments.length));
  41.  
  42.   if (fn.boundSelf_)
  43.     self = fn.boundSelf_;
  44.   if (fn.boundFn_)
  45.     fn = fn.boundFn_;
  46.  
  47.   var newfn = function() {
  48.     // Combine the static args and the new args into one big array
  49.     var args = boundargs.concat(Array.slice(arguments));
  50.     return fn.apply(self, args);
  51.   }
  52.  
  53.   newfn.boundArgs_ = boundargs;
  54.   newfn.boundSelf_ = self;
  55.   newfn.boundFn_ = fn;
  56.  
  57.   return newfn;
  58. }
  59.  
  60. /**
  61.  * Inherit the prototype methods from one constructor into another.
  62.  *
  63.  * Usage:
  64.  *
  65.  * function ParentClass(a, b) { }
  66.  * ParentClass.prototype.foo = function(a) { }
  67.  *
  68.  * function ChildClass(a, b, c) {
  69.  *   ParentClass.call(this, a, b);
  70.  * }
  71.  *
  72.  * ChildClass.inherits(ParentClass);
  73.  *
  74.  * var child = new ChildClass("a", "b", "see");
  75.  * child.foo(); // works
  76.  *
  77.  * In addition, a superclass' implementation of a method can be invoked
  78.  * as follows:
  79.  *
  80.  * ChildClass.prototype.foo = function(a) {
  81.  *   ChildClass.superClass_.foo.call(this, a);
  82.  *   // other code
  83.  * };
  84.  */
  85. Function.prototype.inherits = function(parentCtor) {
  86.   var tempCtor = function(){};
  87.   tempCtor.prototype = parentCtor.prototype;
  88.   this.superClass_ = parentCtor.prototype;
  89.   this.prototype = new tempCtor();
  90. }
  91. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\preferences.js"
  92.  
  93.  
  94. // Class for manipulating preferences. Aside from wrapping the pref
  95. // service, useful functionality includes:
  96. //
  97. // - abstracting prefobserving so that you can observe preferences
  98. //   without implementing nsIObserver 
  99. // 
  100. // - getters that return a default value when the pref doesn't exist 
  101. //   (instead of throwing)
  102. // 
  103. // - get-and-set getters
  104. //
  105. // Example:
  106. // 
  107. // var p = new PROT_Preferences();
  108. // dump(p.getPref("some-true-pref"));     // shows true
  109. // dump(p.getPref("no-such-pref", true)); // shows true   
  110. // dump(p.getPref("no-such-pref", null)); // shows null
  111. //
  112. // function observe(prefThatChanged) {
  113. //   dump("Pref changed: " + prefThatChanged);
  114. // };
  115. //
  116. // p.addObserver("somepref", observe);
  117. // p.setPref("somepref", true);            // dumps
  118. // p.removeObserver("somepref", observe);
  119. //
  120. // TODO: should probably have the prefobserver pass in the new and old
  121. //       values
  122.  
  123. // TODO(tc): Maybe remove this class and just call natively since we're no
  124. //           longer an extension.
  125.  
  126. /**
  127.  * A class that wraps the preferences service.
  128.  *
  129.  * @param opt_startPoint        A starting point on the prefs tree to resolve 
  130.  *                              names passed to setPref and getPref.
  131.  *
  132.  * @param opt_useDefaultPranch  Set to true to work against the default 
  133.  *                              preferences tree instead of the profile one.
  134.  *
  135.  * @constructor
  136.  */
  137. function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
  138.   this.debugZone = "prefs";
  139.   this.observers_ = {};
  140.   this.getDefaultBranch_ = !!opt_getDefaultBranch;
  141.  
  142.   this.startPoint_ = opt_startPoint || null;
  143. }
  144.  
  145. G_Preferences.setterMap_ = { "string": "setCharPref",
  146.                              "boolean": "setBoolPref",
  147.                              "number": "setIntPref" };
  148.  
  149. G_Preferences.getterMap_ = {};
  150. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
  151. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
  152. G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
  153.  
  154. G_Preferences.prototype.__defineGetter__('prefs_', function() {
  155.   var prefs;
  156.   var prefSvc = Cc["@mozilla.org/preferences-service;1"]
  157.                   .getService(Ci.nsIPrefService);
  158.  
  159.   if (this.getDefaultBranch_) {
  160.     prefs = prefSvc.getDefaultBranch(this.startPoint_);
  161.   } else {
  162.     prefs = prefSvc.getBranch(this.startPoint_);
  163.   }
  164.  
  165.   // QI to prefs in case we want to add observers
  166.   prefs.QueryInterface(Ci.nsIPrefBranchInternal);
  167.   return prefs;
  168. });
  169.  
  170. /**
  171.  * Stores a key/value in a user preference. Valid types for val are string,
  172.  * boolean, and number. Complex values are not yet supported (but feel free to
  173.  * add them!).
  174.  */
  175. G_Preferences.prototype.setPref = function(key, val) {
  176.   var datatype = typeof(val);
  177.  
  178.   if (datatype == "number" && (val % 1 != 0)) {
  179.     throw new Error("Cannot store non-integer numbers in preferences.");
  180.   }
  181.  
  182.   var meth = G_Preferences.setterMap_[datatype];
  183.  
  184.   if (!meth) {
  185.     throw new Error("Pref datatype {" + datatype + "} not supported.");
  186.   }
  187.  
  188.   return this.prefs_[meth](key, val);
  189. }
  190.  
  191. /**
  192.  * Retrieves a user preference. Valid types for the value are the same as for
  193.  * setPref. If the preference is not found, opt_default will be returned 
  194.  * instead.
  195.  */
  196. G_Preferences.prototype.getPref = function(key, opt_default) {
  197.   var type = this.prefs_.getPrefType(key);
  198.  
  199.   // zero means that the specified pref didn't exist
  200.   if (type == Ci.nsIPrefBranch.PREF_INVALID) {
  201.     return opt_default;
  202.   }
  203.  
  204.   var meth = G_Preferences.getterMap_[type];
  205.  
  206.   if (!meth) {
  207.     throw new Error("Pref datatype {" + type + "} not supported.");
  208.   }
  209.  
  210.   // If a pref has been cleared, it will have a valid type but won't
  211.   // be gettable, so this will throw.
  212.   try {
  213.     return this.prefs_[meth](key);
  214.   } catch(e) {
  215.     return opt_default;
  216.   }
  217. }
  218.  
  219. /**
  220.  * Delete a preference. 
  221.  *
  222.  * @param which Name of preference to obliterate
  223.  */
  224. G_Preferences.prototype.clearPref = function(which) {
  225.   try {
  226.     // This throws if the pref doesn't exist, which is fine because a 
  227.     // non-existent pref is cleared
  228.     this.prefs_.clearUserPref(which);
  229.   } catch(e) {}
  230. }
  231.  
  232. /**
  233.  * Add an observer for a given pref.
  234.  *
  235.  * @param which String containing the pref to listen to
  236.  * @param callback Function to be called when the pref changes. This
  237.  *                 function will receive a single argument, a string 
  238.  *                 holding the preference name that changed
  239.  */
  240. G_Preferences.prototype.addObserver = function(which, callback) {
  241.   // Need to store the observer we create so we can eventually unregister it
  242.   if (!this.observers_[which])
  243.     this.observers_[which] = { callbacks: [], observers: [] };
  244.  
  245.   /* only add an observer if the callback hasn't been registered yet */
  246.   if (this.observers_[which].callbacks.indexOf(callback) == -1) {
  247.     var observer = new G_PreferenceObserver(callback);
  248.     this.observers_[which].callbacks.push(callback);
  249.     this.observers_[which].observers.push(observer);
  250.     this.prefs_.addObserver(which, observer, false /* strong reference */);
  251.   }
  252. }
  253.  
  254. /**
  255.  * Remove an observer for a given pref.
  256.  *
  257.  * @param which String containing the pref to stop listening to
  258.  * @param callback Function to remove as an observer
  259.  */
  260. G_Preferences.prototype.removeObserver = function(which, callback) {
  261.   var ix = this.observers_[which].callbacks.indexOf(callback);
  262.   G_Assert(this, ix != -1, "Tried to unregister a nonexistant observer"); 
  263.   this.observers_[which].callbacks.splice(ix, 1);
  264.   var observer = this.observers_[which].observers.splice(ix, 1)[0];
  265.   this.prefs_.removeObserver(which, observer);
  266. }
  267.  
  268. /**
  269.  * Remove all preference observers registered through this object.
  270.  */
  271. G_Preferences.prototype.removeAllObservers = function() {
  272.   for (var which in this.observers_) {
  273.     for each (var observer in this.observers_[which].observers) {
  274.       this.prefs_.removeObserver(which, observer);
  275.     }
  276.   }
  277.   this.observers_ = {};
  278. }
  279.  
  280. /**
  281.  * Helper class that knows how to observe preference changes and
  282.  * invoke a callback when they do
  283.  *
  284.  * @constructor
  285.  * @param callback Function to call when the preference changes
  286.  */
  287. function G_PreferenceObserver(callback) {
  288.   this.debugZone = "prefobserver";
  289.   this.callback_ = callback;
  290. }
  291.  
  292. /**
  293.  * Invoked by the pref system when a preference changes. Passes the
  294.  * message along to the callback.
  295.  *
  296.  * @param subject The nsIPrefBranch that changed
  297.  * @param topic String "nsPref:changed" (aka 
  298.  *              NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
  299.  *              live???)
  300.  * @param data Name of the pref that changed
  301.  */
  302. G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
  303.   G_Debug(this, "Observed pref change: " + data);
  304.   this.callback_(data);
  305. }
  306.  
  307. /**
  308.  * XPCOM cruft
  309.  *
  310.  * @param iid Interface id of the interface the caller wants
  311.  */
  312. G_PreferenceObserver.prototype.QueryInterface = function(iid) {
  313.   var Ci = Ci;
  314.   if (iid.equals(Ci.nsISupports) || 
  315.       iid.equals(Ci.nsIObserver) ||
  316.       iid.equals(Ci.nsISupportsWeakReference))
  317.     return this;
  318.   throw Components.results.NS_ERROR_NO_INTERFACE;
  319. }
  320.  
  321. //@line 38 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\debug.js"
  322.  
  323. //@line 868 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\debug.js"
  324.  
  325. // Stubs for the debugging aids scattered through this component.
  326. // They will be expanded if you compile yourself a debug build.
  327.  
  328. function G_Debug(who, msg) { }
  329. function G_Assert(who, condition, msg) { }
  330. function G_Error(who, msg) { }
  331. var G_debugService = { __noSuchMethod__: function() { } };
  332.  
  333. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\alarm.js"
  334.  
  335.  
  336. // An Alarm fires a callback after a certain amount of time, or at
  337. // regular intervals. It's a convenient replacement for
  338. // setTimeout/Interval when you don't want to bind to a specific
  339. // window.
  340. //
  341. // The ConditionalAlarm is an Alarm that cancels itself if its callback 
  342. // returns a value that type-converts to true.
  343. //
  344. // Example:
  345. //
  346. //  function foo() { dump('hi'); };
  347. //  new G_Alarm(foo, 10*1000);                   // Fire foo in 10 seconds
  348. //  new G_Alarm(foo, 10*1000, true /*repeat*/);  // Fire foo every 10 seconds
  349. //  new G_Alarm(foo, 10*1000, true, 7);          // Fire foo every 10 seconds
  350. //                                               // seven times
  351. //  new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true
  352. //
  353. //  // Fire foo every 10 seconds until foo returns true or until it fires seven
  354. //  // times, whichever happens first.
  355. //  new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7);
  356. //
  357. // TODO: maybe pass an isFinal flag to the callback if they opted to
  358. // set maxTimes and this is the last iteration?
  359.  
  360.  
  361. /**
  362.  * Set an alarm to fire after a given amount of time, or at specific 
  363.  * intervals.
  364.  *
  365.  * @param callback Function to call when the alarm fires
  366.  * @param delayMS Number indicating the length of the alarm period in ms
  367.  * @param opt_repeating Boolean indicating whether this should fire 
  368.  *                      periodically
  369.  * @param opt_maxTimes Number indicating a maximum number of times to 
  370.  *                     repeat (obviously only useful when opt_repeating==true)
  371.  */
  372. function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  373.   this.debugZone = "alarm";
  374.   this.callback_ = callback;
  375.   this.repeating_ = !!opt_repeating;
  376.   this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  377.   var type = opt_repeating ? 
  378.              this.timer_.TYPE_REPEATING_SLACK : 
  379.              this.timer_.TYPE_ONE_SHOT;
  380.   this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null;
  381.   this.nTimes_ = 0;
  382.  
  383.   this.observerServiceObserver_ = new G_ObserverServiceObserver(
  384.                                         'xpcom-shutdown',
  385.                                         BindToObject(this.cancel, this));
  386.  
  387.   // Ask the timer to use nsITimerCallback (.notify()) when ready
  388.   this.timer_.initWithCallback(this, delayMS, type);
  389. }
  390.  
  391. /**
  392.  * Cancel this timer 
  393.  */
  394. G_Alarm.prototype.cancel = function() {
  395.   if (!this.timer_) {
  396.     return;
  397.   }
  398.  
  399.   this.timer_.cancel();
  400.   // Break circular reference created between this.timer_ and the G_Alarm
  401.   // instance (this)
  402.   this.timer_ = null;
  403.   this.callback_ = null;
  404.  
  405.   // We don't need the shutdown observer anymore
  406.   this.observerServiceObserver_.unregister();
  407. }
  408.  
  409. /**
  410.  * Invoked by the timer when it fires
  411.  * 
  412.  * @param timer Reference to the nsITimer which fired (not currently 
  413.  *              passed along)
  414.  */
  415. G_Alarm.prototype.notify = function(timer) {
  416.   // fire callback and save results
  417.   var ret = this.callback_();
  418.   
  419.   // If they've given us a max number of times to fire, enforce it
  420.   this.nTimes_++;
  421.   if (this.repeating_ && 
  422.       typeof this.maxTimes_ == "number" 
  423.       && this.nTimes_ >= this.maxTimes_) {
  424.     this.cancel();
  425.   } else if (!this.repeating_) {
  426.     // Clear out the callback closure for TYPE_ONE_SHOT timers
  427.     this.cancel();
  428.   }
  429.   // We don't cancel/cleanup timers that repeat forever until either
  430.   // xpcom-shutdown occurs or cancel() is called explicitly.
  431.  
  432.   return ret;
  433. }
  434.  
  435. G_Alarm.prototype.setDelay = function(delay) {
  436.   this.timer_.delay = delay;
  437. }
  438.  
  439. /**
  440.  * XPCOM cruft
  441.  */
  442. G_Alarm.prototype.QueryInterface = function(iid) {
  443.   if (iid.equals(Components.interfaces.nsISupports) ||
  444.       iid.equals(Components.interfaces.nsITimerCallback))
  445.     return this;
  446.  
  447.   throw Components.results.NS_ERROR_NO_INTERFACE;
  448. }
  449.  
  450.  
  451. /**
  452.  * An alarm with the additional property that it cancels itself if its 
  453.  * callback returns true.
  454.  *
  455.  * For parameter documentation, see G_Alarm
  456.  */
  457. function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  458.   G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes);
  459.   this.debugZone = "conditionalalarm";
  460. }
  461.  
  462. G_ConditionalAlarm.inherits(G_Alarm);
  463.  
  464. /**
  465.  * Invoked by the timer when it fires
  466.  * 
  467.  * @param timer Reference to the nsITimer which fired (not currently 
  468.  *              passed along)
  469.  */
  470. G_ConditionalAlarm.prototype.notify = function(timer) {
  471.   // Call G_Alarm::notify
  472.   var rv = G_Alarm.prototype.notify.call(this, timer);
  473.  
  474.   if (this.repeating_ && rv) {
  475.     G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
  476.     this.cancel();
  477.   }
  478. }
  479. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\cryptohasher.js"
  480.  
  481.  
  482. // A very thin wrapper around nsICryptoHash. It's not strictly
  483. // necessary, but makes the code a bit cleaner and gives us the
  484. // opportunity to verify that our implementations give the results that
  485. // we expect, for example if we have to interoperate with a server.
  486. //
  487. // The digest* methods reset the state of the hasher, so it's
  488. // necessary to call init() explicitly after them.
  489. //
  490. // Works only in Firefox 1.5+.
  491. //
  492. // IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
  493. // you cannot use the cryptohasher before app-startup. The symptom of doing
  494. // so is a segfault in NSS.
  495.  
  496. /**
  497.  * Instantiate a new hasher. You must explicitly call init() before use!
  498.  */
  499. function G_CryptoHasher() {
  500.   this.debugZone = "cryptohasher";
  501.   this.hasher_ = null;
  502. }
  503.  
  504. G_CryptoHasher.algorithms = {
  505.   MD2: Ci.nsICryptoHash.MD2,
  506.   MD5: Ci.nsICryptoHash.MD5,
  507.   SHA1: Ci.nsICryptoHash.SHA1,
  508.   SHA256: Ci.nsICryptoHash.SHA256,
  509.   SHA384: Ci.nsICryptoHash.SHA384,
  510.   SHA512: Ci.nsICryptoHash.SHA512,
  511. };
  512.  
  513. /**
  514.  * Initialize the hasher. This function must be called after every call
  515.  * to one of the digest* methods.
  516.  *
  517.  * @param algorithm Constant from G_CryptoHasher.algorithms specifying the
  518.  *                  algorithm this hasher will use
  519.  */ 
  520. G_CryptoHasher.prototype.init = function(algorithm) {
  521.   var validAlgorithm = false;
  522.   for (var alg in G_CryptoHasher.algorithms)
  523.     if (algorithm == G_CryptoHasher.algorithms[alg])
  524.       validAlgorithm = true;
  525.  
  526.   if (!validAlgorithm)
  527.     throw new Error("Invalid algorithm: " + algorithm);
  528.  
  529.   this.hasher_ = Cc["@mozilla.org/security/hash;1"]
  530.                  .createInstance(Ci.nsICryptoHash);
  531.   this.hasher_.init(algorithm);
  532. }
  533.  
  534. /**
  535.  * Update the hash's internal state with input given in a string. Can be
  536.  * called multiple times for incrementeal hash updates.
  537.  *
  538.  * @param input String containing data to hash.
  539.  */ 
  540. G_CryptoHasher.prototype.updateFromString = function(input) {
  541.   if (!this.hasher_)
  542.     throw new Error("You must initialize the hasher first!");
  543.  
  544.   var stream = Cc['@mozilla.org/io/string-input-stream;1']
  545.                .createInstance(Ci.nsIStringInputStream);
  546.   stream.setData(input, input.length);
  547.   this.updateFromStream(stream);
  548. }
  549.  
  550. /**
  551.  * Update the hash's internal state with input given in an array. Can be
  552.  * called multiple times for incremental hash updates.
  553.  *
  554.  * @param input Array containing data to hash.
  555.  */ 
  556. G_CryptoHasher.prototype.updateFromArray = function(input) {
  557.   if (!this.hasher_)
  558.     throw new Error("You must initialize the hasher first!");
  559.  
  560.   this.hasher_.update(input, input.length);
  561. }
  562.  
  563. /**
  564.  * Update the hash's internal state with input given in a stream. Can be
  565.  * called multiple times from incremental hash updates.
  566.  */
  567. G_CryptoHasher.prototype.updateFromStream = function(stream) {
  568.   if (!this.hasher_)
  569.     throw new Error("You must initialize the hasher first!");
  570.  
  571.   if (stream.available())
  572.     this.hasher_.updateFromStream(stream, stream.available());
  573. }
  574.  
  575. /**
  576.  * @returns The hash value as a string (sequence of 8-bit values)
  577.  */ 
  578. G_CryptoHasher.prototype.digestRaw = function() {
  579.   var digest = this.hasher_.finish(false /* not b64 encoded */);
  580.   this.hasher_ = null;
  581.   return digest;
  582. }
  583.  
  584. /**
  585.  * @returns The hash value as a base64-encoded string
  586.  */ 
  587. G_CryptoHasher.prototype.digestBase64 = function() {
  588.   var digest = this.hasher_.finish(true /* b64 encoded */);
  589.   this.hasher_ = null;
  590.   return digest;
  591. }
  592.  
  593. /**
  594.  * @returns The hash value as a hex-encoded string
  595.  */ 
  596. G_CryptoHasher.prototype.digestHex = function() {
  597.   var raw = this.digestRaw();
  598.   return this.toHex_(raw);
  599. }
  600.  
  601. /**
  602.  * Converts a sequence of values to a hex-encoded string. The input is a
  603.  * a string, so you can stick 16-bit values in each character.
  604.  *
  605.  * @param str String to conver to hex. (Often this is just a sequence of
  606.  *            16-bit values)
  607.  *
  608.  * @returns String containing the hex representation of the input
  609.  */ 
  610. G_CryptoHasher.prototype.toHex_ = function(str) {
  611.   var hexchars = '0123456789ABCDEF';
  612.   var hexrep = new Array(str.length * 2);
  613.  
  614.   for (var i = 0; i < str.length; ++i) {
  615.     hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
  616.     hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
  617.   }
  618.   return hexrep.join('');
  619. }
  620.  
  621. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\observer.js"
  622.  
  623.  
  624. // A couple of classes to simplify creating observers. 
  625. //
  626. // // Example1:
  627. //
  628. // function doSomething() { ... }
  629. // var observer = new G_ObserverWrapper(topic, doSomething);
  630. // someObj.addObserver(topic, observer);
  631. //
  632. // // Example2: 
  633. //
  634. // function doSomething() { ... }
  635. // new G_ObserverServiceObserver("profile-after-change", 
  636. //                               doSomething,
  637. //                               true /* run only once */);
  638.  
  639.  
  640. /**
  641.  * This class abstracts the admittedly simple boilerplate required of
  642.  * an nsIObserver. It saves you the trouble of implementing the
  643.  * indirection of your own observe() function.
  644.  *
  645.  * @param topic String containing the topic the observer will filter for
  646.  *
  647.  * @param observeFunction Reference to the function to call when the 
  648.  *                        observer fires
  649.  *
  650.  * @constructor
  651.  */
  652. function G_ObserverWrapper(topic, observeFunction) {
  653.   this.debugZone = "observer";
  654.   this.topic_ = topic;
  655.   this.observeFunction_ = observeFunction;
  656. }
  657.  
  658. /**
  659.  * XPCOM
  660.  */
  661. G_ObserverWrapper.prototype.QueryInterface = function(iid) {
  662.   if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
  663.     return this;
  664.   throw Components.results.NS_ERROR_NO_INTERFACE;
  665. }
  666.  
  667. /**
  668.  * Invoked by the thingy being observed
  669.  */
  670. G_ObserverWrapper.prototype.observe = function(subject, topic, data) {
  671.   if (topic == this.topic_)
  672.     this.observeFunction_(subject, topic, data);
  673. }
  674.  
  675.  
  676. /**
  677.  * This class abstracts the admittedly simple boilerplate required of
  678.  * observing an observerservice topic. It implements the indirection
  679.  * required, and automatically registers to hear the topic.
  680.  *
  681.  * @param topic String containing the topic the observer will filter for
  682.  *
  683.  * @param observeFunction Reference to the function to call when the 
  684.  *                        observer fires
  685.  *
  686.  * @param opt_onlyOnce Boolean indicating if the observer should unregister
  687.  *                     after it has fired
  688.  *
  689.  * @constructor
  690.  */
  691. function G_ObserverServiceObserver(topic, observeFunction, opt_onlyOnce) {
  692.   this.debugZone = "observerserviceobserver";
  693.   this.topic_ = topic;
  694.   this.observeFunction_ = observeFunction;
  695.   this.onlyOnce_ = !!opt_onlyOnce;
  696.   
  697.   this.observer_ = new G_ObserverWrapper(this.topic_, 
  698.                                          BindToObject(this.observe_, this));
  699.   this.observerService_ = Cc["@mozilla.org/observer-service;1"]
  700.                           .getService(Ci.nsIObserverService);
  701.   this.observerService_.addObserver(this.observer_, this.topic_, false);
  702. }
  703.  
  704. /**
  705.  * Unregister the observer from the observerservice
  706.  */
  707. G_ObserverServiceObserver.prototype.unregister = function() {
  708.   this.observerService_.removeObserver(this.observer_, this.topic_);
  709.   this.observerService_ = null;
  710. }
  711.  
  712. /**
  713.  * Invoked by the observerservice
  714.  */
  715. G_ObserverServiceObserver.prototype.observe_ = function(subject, topic, data) {
  716.   this.observeFunction_(subject, topic, data);
  717.   if (this.onlyOnce_)
  718.     this.unregister();
  719. }
  720.  
  721. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\moz\protocol4.js"
  722.  
  723.  
  724. // A helper class that knows how to parse from and serialize to
  725. // protocol4. This is a simple, historical format used by some Google
  726. // interfaces, for example the Toolbar (i.e., ancient services).
  727. //
  728. // Protocol4 consists of a newline-separated sequence of name/value
  729. // pairs (strings). Each line consists of the name, the value length,
  730. // and the value itself, all separated by colons. Example:
  731. //
  732. // foo:6:barbaz\n
  733. // fritz:33:issickofdynamicallytypedlanguages\n
  734.  
  735.  
  736. /**
  737.  * This class knows how to serialize/deserialize maps to/from their
  738.  * protocol4 representation.
  739.  *
  740.  * @constructor
  741.  */
  742. function G_Protocol4Parser() {
  743.   this.debugZone = "protocol4";
  744.  
  745.   this.protocol4RegExp_ = new RegExp("([^:]+):\\d+:(.*)$");
  746.   this.newlineRegExp_ = new RegExp("(\\r)?\\n");
  747. }
  748.  
  749. /**
  750.  * Create a map from a protocol4 string. Silently skips invalid lines.
  751.  *
  752.  * @param text String holding the protocol4 representation
  753.  * 
  754.  * @returns Object as an associative array with keys and values 
  755.  *          given in text. The empty object is returned if none
  756.  *          are parsed.
  757.  */
  758. G_Protocol4Parser.prototype.parse = function(text) {
  759.  
  760.   var response = {};
  761.   if (!text)
  762.     return response;
  763.  
  764.   // Responses are protocol4: (repeated) name:numcontentbytes:content\n
  765.   var lines = text.split(this.newlineRegExp_);
  766.   for (var i = 0; i < lines.length; i++)
  767.     if (this.protocol4RegExp_.exec(lines[i]))
  768.       response[RegExp.$1] = RegExp.$2;
  769.  
  770.   return response;
  771. }
  772.  
  773. /**
  774.  * Create a protocol4 string from a map (object). Throws an error on 
  775.  * an invalid input.
  776.  *
  777.  * @param map Object as an associative array with keys and values 
  778.  *            given as strings.
  779.  *
  780.  * @returns text String holding the protocol4 representation
  781.  */
  782. G_Protocol4Parser.prototype.serialize = function(map) {
  783.   if (typeof map != "object")
  784.     throw new Error("map must be an object");
  785.  
  786.   var text = "";
  787.   for (var key in map) {
  788.     if (typeof map[key] != "string")
  789.       throw new Error("Keys and values must be strings");
  790.     
  791.     text += key + ":" + map[key].length + ":" + map[key] + "\n";
  792.   }
  793.   
  794.   return text;
  795. }
  796.  
  797. //@line 53 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\src\nsUrlClassifierLib.js"
  798.  
  799. /* ***** BEGIN LICENSE BLOCK *****
  800.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  801.  *
  802.  * The contents of this file are subject to the Mozilla Public License Version
  803.  * 1.1 (the "License"); you may not use this file except in compliance with
  804.  * the License. You may obtain a copy of the License at
  805.  * http://www.mozilla.org/MPL/
  806.  *
  807.  * Software distributed under the License is distributed on an "AS IS" basis,
  808.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  809.  * for the specific language governing rights and limitations under the
  810.  * License.
  811.  *
  812.  * The Original Code is Google Safe Browsing.
  813.  *
  814.  * The Initial Developer of the Original Code is Google Inc.
  815.  * Portions created by the Initial Developer are Copyright (C) 2006
  816.  * the Initial Developer. All Rights Reserved.
  817.  *
  818.  * Contributor(s):
  819.  *   Tony Chang <tc@google.com> (original author)
  820.  *
  821.  * Alternatively, the contents of this file may be used under the terms of
  822.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  823.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  824.  * in which case the provisions of the GPL or the LGPL are applicable instead
  825.  * of those above. If you wish to allow use of your version of this file only
  826.  * under the terms of either the GPL or the LGPL, and not to allow others to
  827.  * use your version of this file under the terms of the MPL, indicate your
  828.  * decision by deleting the provisions above and replace them with the notice
  829.  * and other provisions required by the GPL or the LGPL. If you do not delete
  830.  * the provisions above, a recipient may use your version of this file under
  831.  * the terms of any one of the MPL, the GPL or the LGPL.
  832.  *
  833.  * ***** END LICENSE BLOCK ***** */
  834.  
  835. // This implements logic for stopping requests if the server starts to return
  836. // too many errors.  If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
  837. // back off for TIMEOUT_INCREMENT minutes.  If we get another error
  838. // immediately after we restart, we double the timeout and add
  839. // TIMEOUT_INCREMENT minutes, etc.
  840. // 
  841. // This is similar to the logic used by the search suggestion service.
  842.  
  843. // HTTP responses that count as an error.  We also include any 5xx response
  844. // as an error.
  845. const HTTP_FOUND                 = 302;
  846. const HTTP_SEE_OTHER             = 303;
  847. const HTTP_TEMPORARY_REDIRECT    = 307;
  848.  
  849. /**
  850.  * @param maxErrors Number the number of errors needed to trigger backoff
  851.  * @param errorPeriod Number time (ms) in which maxErros have to occur to
  852.  *     trigger the backoff behavior
  853.  * @param timeoutIncrement Number time (ms) the starting timeout period
  854.  *     we double this time for consecutive errors
  855.  * @param maxTimeout Number time (ms) maximum timeout period
  856.  */
  857. function RequestBackoff(maxErrors, errorPeriod, timeoutIncrement, maxTimeout) {
  858.   this.MAX_ERRORS_ = maxErrors;
  859.   this.ERROR_PERIOD_ = errorPeriod;
  860.   this.TIMEOUT_INCREMENT_ = timeoutIncrement;
  861.   this.MAX_TIMEOUT_ = maxTimeout;
  862.  
  863.   // Queue of ints keeping the time of errors.
  864.   this.errorTimes_ = [];
  865.   this.errorTimeout_ = 0;
  866.   this.nextRequestTime_ = 0;
  867.   this.backoffTriggered_ = false;
  868. }
  869.  
  870. /**
  871.  * Reset the object for reuse.
  872.  */
  873. RequestBackoff.prototype.reset = function() {
  874.   this.errorTimes_ = [];
  875.   this.errorTimeout_ = 0;
  876.   this.nextRequestTime_ = 0;
  877.   this.backoffTriggered_ = false;
  878. }
  879.  
  880. /**
  881.  * Check to see if we can make a request.
  882.  */
  883. RequestBackoff.prototype.canMakeRequest = function() {
  884.   return Date.now() > this.nextRequestTime_;
  885. }
  886.  
  887. /**
  888.  * Notify this object of the last server response.  If it's an error,
  889.  */
  890. RequestBackoff.prototype.noteServerResponse = function(status) {
  891.   if (this.isErrorStatus_(status)) {
  892.     var now = Date.now();
  893.     this.errorTimes_.push(now);
  894.  
  895.     // We only care about keeping track of MAX_ERRORS
  896.     if (this.errorTimes_.length > this.MAX_ERRORS_)
  897.       this.errorTimes_.shift();
  898.  
  899.     // See if we hit the backoff case
  900.     // This either means we hit MAX_ERRORS in ERROR_PERIOD
  901.     // *or* we were already in a backoff state, in which case we
  902.     // increase our timeout.
  903.     if ((this.errorTimes_.length == this.MAX_ERRORS_ &&
  904.          now - this.errorTimes_[0] < this.ERROR_PERIOD_)
  905.         || this.backoffTriggered_) {
  906.       this.errorTimeout_ = (this.errorTimeout_ * 2)  + this.TIMEOUT_INCREMENT_;
  907.       this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
  908.       this.nextRequestTime_ = now + this.errorTimeout_;
  909.       this.backoffTriggered_ = true;
  910.     }
  911.   } else {
  912.     // Reset error timeout, allow requests to go through, and switch out
  913.     // of backoff state.
  914.     this.errorTimeout_ = 0;
  915.     this.nextRequestTime_ = 0;
  916.     this.backoffTriggered_ = false;
  917.   }
  918. }
  919.  
  920. /**
  921.  * We consider 302, 303, 307, and 5xx http responses to be errors.
  922.  * @param status Number http status
  923.  * @return Boolean true if we consider this http status an error
  924.  */
  925. RequestBackoff.prototype.isErrorStatus_ = function(status) {
  926.   return ((500 <= status && status <= 599) ||
  927.           HTTP_FOUND == status ||
  928.           HTTP_SEE_OTHER == status ||
  929.           HTTP_TEMPORARY_REDIRECT == status);
  930. }
  931.  
  932. //@line 37 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\url-crypto.js"
  933.  
  934.  
  935. // This file implements our query param encryption. You hand it a set
  936. // of query params, and it will hand you a set of (maybe) encrypted
  937. // query params back. It takes the query params you give it, 
  938. // encodes and encrypts them into a encrypted query param, and adds
  939. // the extra query params the server will need to decrypt them
  940. // (e.g., the version of encryption and the decryption key).
  941. // 
  942. // The key manager provides the keys we need; this class just focuses
  943. // on encrypting query params. See the url crypto key manager for
  944. // details of our protocol, but essentially encryption is
  945. // RC4_key(input) with key == MD5(K_C || nonce) where nonce is a
  946. // 32-bit integer appended big-endian and K_C is the client's key.
  947. //
  948. // If for some reason we don't have an encryption key, encrypting is the 
  949. // identity function.
  950.  
  951. /**
  952.  * This class knows how to encrypt query parameters that will be
  953.  * understood by the lookupserver.
  954.  * 
  955.  * @constructor
  956.  */
  957. function PROT_UrlCrypto() {
  958.   this.debugZone = "urlcrypto";
  959.   this.hasher_ = new G_CryptoHasher();
  960.   this.streamCipher_ = Cc["@mozilla.org/security/streamcipher;1"]
  961.                        .createInstance(Ci.nsIStreamCipher);
  962.  
  963.   if (!this.manager_) {
  964.     // Create a UrlCryptoKeyManager to reads keys from profile directory if
  965.     // one doesn't already exist.  UrlCryptoKeyManager puts a reference to
  966.     // itself on PROT_UrlCrypto.prototype (this also prevents garbage
  967.     // collection).
  968.     new PROT_UrlCryptoKeyManager();
  969.   }
  970.  
  971.   // Convenience properties
  972.   this.VERSION = PROT_UrlCrypto.VERSION;
  973.   this.RC4_DISCARD_BYTES = PROT_UrlCrypto.RC4_DISCARD_BYTES;
  974.   this.VERSION_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME;
  975.   this.ENCRYPTED_PARAMS_PARAM_NAME = 
  976.     PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME;
  977.   this.COUNT_QUERY_PARAM_NAME = PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME;
  978.   this.WRAPPEDKEY_QUERY_PARAM_NAME = 
  979.     PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME;
  980.  
  981.   // Properties for computing macs
  982.   this.macer_ = new G_CryptoHasher(); // don't use hasher_
  983.   this.macInitialized_ = false;
  984.   // Separator to prevent leakage between key and data when computing mac
  985.   this.separator_ = ":coolgoog:";
  986.   this.separatorArray_ = Array.map(this.separator_,
  987.                                    function(c) { return c.charCodeAt(0); });
  988. }
  989.  
  990. // The version of encryption we implement
  991. PROT_UrlCrypto.VERSION = "1";
  992.  
  993. PROT_UrlCrypto.RC4_DISCARD_BYTES = 1600;
  994.  
  995. // The query params are we going to send to let the server know what is
  996. // encrypted, and how
  997. PROT_UrlCrypto.QPS = {};
  998. PROT_UrlCrypto.QPS.VERSION_QUERY_PARAM_NAME = "encver";
  999. PROT_UrlCrypto.QPS.ENCRYPTED_PARAMS_PARAM_NAME = "encparams";
  1000. PROT_UrlCrypto.QPS.COUNT_QUERY_PARAM_NAME = "nonce";
  1001. PROT_UrlCrypto.QPS.WRAPPEDKEY_QUERY_PARAM_NAME = "wrkey";
  1002.  
  1003. /**
  1004.  * @returns Reference to the keymanager (if one exists), else undefined
  1005.  */
  1006. PROT_UrlCrypto.prototype.getManager = function() {
  1007.   return this.manager_;
  1008. }
  1009.  
  1010. /**
  1011.  * Helper method that takes a map of query params (param name ->
  1012.  * value) and turns them into a query string. Note that it encodes
  1013.  * the values as it writes the string.
  1014.  *
  1015.  * @param params Object (map) of query names to values. Values should
  1016.  *               not be uriencoded.
  1017.  *
  1018.  * @returns String of query params from the map. Values will be uri
  1019.  *          encoded
  1020.  */
  1021. PROT_UrlCrypto.prototype.appendParams_ = function(params) {
  1022.   var queryString = "";
  1023.   for (var param in params)
  1024.     queryString += "&" + param + "=" + encodeURIComponent(params[param]);
  1025.                    
  1026.   return queryString;
  1027. }
  1028.  
  1029. /**
  1030.  * Encrypt a set of query params if we can. If we can, we return a new
  1031.  * set of query params that should be added to a query string. The set
  1032.  * of query params WILL BE different than the input query params if we
  1033.  * can encrypt (e.g., there will be extra query params with meta-
  1034.  * information such as the version of encryption we're using). If we
  1035.  * can't encrypt, we just return the query params we're passed.
  1036.  *
  1037.  * @param params Object (map) of query param names to values. Values should
  1038.  *               not be uriencoded.
  1039.  *
  1040.  * @returns Object (map) of query param names to values. Values are NOT
  1041.  *          uriencoded; the caller should encode them as it writes them
  1042.  *          to a proper query string.
  1043.  */
  1044. PROT_UrlCrypto.prototype.maybeCryptParams = function(params) {
  1045.   if (!this.manager_)
  1046.     throw new Error("Need a key manager for UrlCrypto");
  1047.   if (typeof params != "object")
  1048.     throw new Error("params is an associative array of name/value params");
  1049.  
  1050.   var clientKeyArray = this.manager_.getClientKeyArray();
  1051.   var wrappedKey = this.manager_.getWrappedKey();
  1052.  
  1053.   // No keys? Can't encrypt. Damn.
  1054.   if (!clientKeyArray || !wrappedKey) {
  1055.     G_Debug(this, "No key; can't encrypt query params");
  1056.     return params;
  1057.   }
  1058.  
  1059.   // Serialize query params to a query string that we will then
  1060.   // encrypt and place in a special query param the front-end knows is
  1061.   // encrypted.
  1062.   var queryString = this.appendParams_(params);
  1063.   
  1064.   // Nonce, really. We want 32 bits; make it so.
  1065.   var counter = this.getCount_();
  1066.   counter = counter & 0xFFFFFFFF;
  1067.   
  1068.   var encrypted = this.encryptV1(clientKeyArray, 
  1069.                                  this.VERSION,
  1070.                                  counter,
  1071.                                  queryString);
  1072.  
  1073.   params = {};
  1074.   params[this.VERSION_QUERY_PARAM_NAME] = this.VERSION;
  1075.   params[this.COUNT_QUERY_PARAM_NAME] = counter;
  1076.   params[this.WRAPPEDKEY_QUERY_PARAM_NAME] = wrappedKey;
  1077.   params[this.ENCRYPTED_PARAMS_PARAM_NAME] = encrypted;
  1078.  
  1079.   return params;
  1080. }
  1081.  
  1082. /**
  1083.  * Encrypts text and returns a base64 string of the results.
  1084.  *
  1085.  * This method runs in about ~2ms on a 2Ghz P4. (Turn debugging off if
  1086.  * you see it much slower).
  1087.  *
  1088.  * @param clientKeyArray Array of bytes (numbers in [0,255]) composing K_C
  1089.  *
  1090.  * @param version String indicating the version of encryption we should use.
  1091.  *
  1092.  * @param counter Number that acts as a nonce for this encryption
  1093.  *
  1094.  * @param text String to be encrypted
  1095.  *
  1096.  * @returns String containing the websafe base64-encoded ciphertext
  1097.  */
  1098. PROT_UrlCrypto.prototype.encryptV1 = function(clientKeyArray,
  1099.                                               version, 
  1100.                                               counter,
  1101.                                               text) {
  1102.  
  1103.   // We're a version1 encrypter, after all
  1104.   if (version != "1") 
  1105.     throw new Error("Unknown encryption version");
  1106.  
  1107.   var key = this.deriveEncryptionKey(clientKeyArray, counter);
  1108.  
  1109.   this.streamCipher_.init(key);
  1110.  
  1111.   if (this.RC4_DISCARD_BYTES > 0)
  1112.     this.streamCipher_.discard(this.RC4_DISCARD_BYTES);
  1113.   
  1114.   this.streamCipher_.updateFromString(text);
  1115.  
  1116.   var encrypted = this.streamCipher_.finish(true /* base64 encoded */);
  1117.   // The base64 version we get has new lines, we want to remove those.
  1118.   
  1119.   return encrypted.replace(/\r\n/g, "");
  1120. }
  1121.   
  1122. /**
  1123.  * Create an encryption key from K_C and a nonce
  1124.  *
  1125.  * @param clientKeyArray Array of bytes comprising K_C
  1126.  *
  1127.  * @param count Number that acts as a nonce for this key
  1128.  *
  1129.  * @return nsIKeyObject
  1130.  */
  1131. PROT_UrlCrypto.prototype.deriveEncryptionKey = function(clientKeyArray, 
  1132.                                                         count) {
  1133.   G_Assert(this, clientKeyArray instanceof Array,
  1134.            "Client key should be an array of bytes");
  1135.   G_Assert(this, typeof count == "number", "Count should be a number");
  1136.   
  1137.   // Don't clobber the client key by appending the nonce; use another array
  1138.   var paddingArray = [];
  1139.   paddingArray.push(count >> 24);
  1140.   paddingArray.push((count >> 16) & 0xFF);
  1141.   paddingArray.push((count >> 8) & 0xFF);
  1142.   paddingArray.push(count & 0xFF);
  1143.  
  1144.   this.hasher_.init(G_CryptoHasher.algorithms.MD5);
  1145.   this.hasher_.updateFromArray(clientKeyArray);
  1146.   this.hasher_.updateFromArray(paddingArray);
  1147.  
  1148.   // Create the nsIKeyObject
  1149.   var keyFactory = Cc["@mozilla.org/security/keyobjectfactory;1"]
  1150.                    .getService(Ci.nsIKeyObjectFactory);
  1151.   var key = keyFactory.keyFromString(Ci.nsIKeyObject.RC4,
  1152.                                      this.hasher_.digestRaw());
  1153.   return key;
  1154. }
  1155.  
  1156. /**
  1157.  * Return a new nonce for us to use. Rather than keeping a counter and
  1158.  * the headaches that entails, just use the low ms since the epoch.
  1159.  *
  1160.  * @returns 32-bit number that is the nonce to use for this encryption
  1161.  */
  1162. PROT_UrlCrypto.prototype.getCount_ = function() {
  1163.   return ((new Date).getTime() & 0xFFFFFFFF);
  1164. }
  1165.  
  1166. /**
  1167.  * Init the mac.  This function is called by WireFormatReader if the update
  1168.  * server has sent along a mac param.  The caller must not call initMac again
  1169.  * before calling finishMac; instead, the caller should just use another
  1170.  * UrlCrypto object.
  1171.  *
  1172.  * @param opt_clientKeyArray Optional clientKeyArray, for testing
  1173.  */
  1174. PROT_UrlCrypto.prototype.initMac = function(opt_clientKeyArray) {
  1175.   if (this.macInitialized_) {
  1176.     throw new Error("Can't interleave calls to initMac.  Please use another " +
  1177.                     "UrlCrypto object.");
  1178.   }
  1179.  
  1180.   this.macInitialized_ = true;
  1181.  
  1182.   var clientKeyArray = null;
  1183.  
  1184.   if (!!opt_clientKeyArray) {
  1185.     clientKeyArray = opt_clientKeyArray;
  1186.   } else {
  1187.     clientKeyArray = this.manager_.getClientKeyArray();
  1188.   }
  1189.  
  1190.   // Don't re-use this.hasher_, in case someone calls deriveEncryptionKey
  1191.   // between initMac and finishMac
  1192.   this.macer_.init(G_CryptoHasher.algorithms.MD5);
  1193.  
  1194.   this.macer_.updateFromArray(clientKeyArray);
  1195.   this.macer_.updateFromArray(this.separatorArray_);
  1196. }
  1197.  
  1198. /**
  1199.  * Add a line to the mac.  Called by WireFormatReader.processLine.  Not thread
  1200.  * safe.
  1201.  *
  1202.  * @param s The string to add
  1203.  */
  1204. PROT_UrlCrypto.prototype.updateMacFromString = function(s) {
  1205.   if (!this.macInitialized_) {
  1206.     throw new Error ("Initialize mac first");
  1207.   }
  1208.  
  1209.   var stream = Cc['@mozilla.org/io/string-input-stream;1']
  1210.                .createInstance(Ci.nsIStringInputStream);
  1211.   stream.setData(s, s.length);
  1212.   if (stream.available() > 0)
  1213.     this.macer_.updateFromStream(stream);
  1214. }
  1215.  
  1216. /**
  1217.  * Finish up computing the mac.  Not thread safe.
  1218.  *
  1219.  * @param opt_clientKeyArray Optional clientKeyArray, for testing
  1220.  */
  1221. PROT_UrlCrypto.prototype.finishMac = function(opt_clientKeyArray) {
  1222.   var clientKeyArray = null;
  1223.   if (!!opt_clientKeyArray) {
  1224.     clientKeyArray = opt_clientKeyArray;
  1225.   } else {
  1226.     clientKeyArray = this.manager_.getClientKeyArray();
  1227.   }
  1228.  
  1229.   if (!this.macInitialized_) {
  1230.     throw new Error ("Initialize mac first");
  1231.   }
  1232.   this.macer_.updateFromArray(this.separatorArray_);
  1233.   this.macer_.updateFromArray(clientKeyArray);
  1234.  
  1235.   this.macInitialized_ = false;
  1236.  
  1237.   return this.macer_.digestBase64();
  1238. }
  1239.  
  1240. /**
  1241.  * Compute a mac over the whole data string, and return the base64-encoded
  1242.  * string
  1243.  *
  1244.  * @param data A string
  1245.  * @param opt_outputRaw True for raw output, false for base64
  1246.  * @param opt_clientKeyArray An optional key to pass in for testing
  1247.  * @param opt_separatorArray An optional separator array to pass in for testing
  1248.  * @returns MD5(key+separator+data+separator+key)
  1249.  */
  1250. PROT_UrlCrypto.prototype.computeMac = function(data, 
  1251.                                                opt_outputRaw,
  1252.                                                opt_clientKeyArray,
  1253.                                                opt_separatorArray) {
  1254.   var clientKeyArray = null;
  1255.   var separatorArray = null;
  1256.  
  1257.   // Get keys and such for testing
  1258.   if (!!opt_clientKeyArray) {
  1259.     clientKeyArray = opt_clientKeyArray;
  1260.   } else {
  1261.     clientKeyArray = this.manager_.getClientKeyArray();
  1262.   }
  1263.  
  1264.   if (!!opt_separatorArray) {
  1265.     separatorArray = opt_separatorArray;
  1266.   } else {
  1267.     separatorArray = this.separatorArray_;
  1268.   }
  1269.  
  1270.   this.macer_.init(G_CryptoHasher.algorithms.MD5);
  1271.  
  1272.   this.macer_.updateFromArray(clientKeyArray);
  1273.   this.macer_.updateFromArray(separatorArray);
  1274.  
  1275.   var stream = Cc['@mozilla.org/io/string-input-stream;1']
  1276.                .createInstance(Ci.nsIStringInputStream);  
  1277.   stream.setData(data, data.length);
  1278.   if (stream.available() > 0)
  1279.     this.macer_.updateFromStream(stream);
  1280.  
  1281.   this.macer_.updateFromArray(separatorArray);
  1282.   this.macer_.updateFromArray(clientKeyArray);
  1283.  
  1284.   if (!!opt_outputRaw) {
  1285.     return this.macer_.digestRaw();
  1286.   }
  1287.   return this.macer_.digestBase64();
  1288. }
  1289.  
  1290. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\url-crypto-key-manager.js"
  1291.  
  1292.  
  1293. // This file implements the tricky business of managing the keys for our 
  1294. // URL encryption. The protocol is:
  1295. //
  1296. // - Server generates secret key K_S
  1297. // - Client starts up and requests a new key K_C from the server via HTTPS
  1298. // - Server generates K_C and WrappedKey, which is K_C encrypted with K_S
  1299. // - Server resonse with K_C and WrappedKey
  1300. // - When client wants to encrypt a URL, it encrypts it with K_C and sends
  1301. //   the encrypted URL along with WrappedKey
  1302. // - Server decrypts WrappedKey with K_S to get K_C, and the URL with K_C
  1303. //
  1304. // This is, however, trickier than it sounds for two reasons. First,
  1305. // we want to keep the number of HTTPS requests to an aboslute minimum
  1306. // (like 1 or 2 per browser session). Second, the HTTPS request at
  1307. // startup might fail, for example the user might be offline or a URL
  1308. // fetch might need to be issued before the HTTPS request has
  1309. // completed.
  1310. //
  1311. // We implement the following policy:
  1312. // 
  1313. // - Firefox will issue at most two HTTPS getkey requests per session
  1314. // - Firefox will issue one HTTPS getkey request at startup if more than 24
  1315. //   hours has passed since the last getkey request.
  1316. // - Firefox will serialize to disk any key it gets
  1317. // - Firefox will fall back on this serialized key until it has a
  1318. //   fresh key
  1319. // - The front-end can respond with a flag in a lookup request that tells
  1320. //   the client to re-key. Firefox will issue a new HTTPS getkey request
  1321. //   at this time if it has only issued one before
  1322.  
  1323. // We store the user key in this file.  The key can be used to verify signed
  1324. // server updates.
  1325. const kKeyFilename = "kf.txt";
  1326.  
  1327. /**
  1328.  * A key manager for UrlCrypto. There should be exactly one of these
  1329.  * per appplication, and all UrlCrypto's should share it. This is
  1330.  * currently implemented by having the manager attach itself to the
  1331.  * UrlCrypto's prototype at startup. We could've opted for a global
  1332.  * instead, but I like this better, even though it is spooky action
  1333.  * at a distance.
  1334.  * XXX: Should be an XPCOM service
  1335.  *
  1336.  * @param opt_keyFilename String containing the name of the 
  1337.  *                        file we should serialize keys to/from. Used
  1338.  *                        mostly for testing.
  1339.  *
  1340.  * @param opt_testing Boolean indicating whether we are testing. If we 
  1341.  *                    are, then we skip trying to read the old key from
  1342.  *                    file and automatically trying to rekey; presumably
  1343.  *                    the tester will drive these manually.
  1344.  *
  1345.  * @constructor
  1346.  */
  1347. function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
  1348.   this.debugZone = "urlcryptokeymanager";
  1349.   this.testing_ = !!opt_testing;
  1350.   this.clientKey_ = null;          // Base64-encoded, as fetched from server
  1351.   this.clientKeyArray_ = null;     // Base64-decoded into an array of numbers
  1352.   this.wrappedKey_ = null;         // Opaque websafe base64-encoded server key
  1353.   this.rekeyTries_ = 0;
  1354.  
  1355.   // Don't do anything until keyUrl_ is set.
  1356.   this.keyUrl_ = null;
  1357.  
  1358.   this.keyFilename_ = opt_keyFilename ? 
  1359.                       opt_keyFilename : kKeyFilename;
  1360.  
  1361.   // Convenience properties
  1362.   this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
  1363.   this.CLIENT_KEY_NAME = PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME;
  1364.   this.WRAPPED_KEY_NAME = PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME;
  1365.  
  1366.   if (!this.testing_) {
  1367.     G_Assert(this, !PROT_UrlCrypto.prototype.manager_,
  1368.              "Already have manager?");
  1369.     PROT_UrlCrypto.prototype.manager_ = this;
  1370.  
  1371.     this.maybeLoadOldKey();
  1372.   }
  1373. }
  1374.  
  1375. // Do ***** NOT ***** set this higher; HTTPS is expensive
  1376. PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES = 2;
  1377.  
  1378. // Base pref for keeping track of when we updated our key.
  1379. // We store the time as seconds since the epoch.
  1380. PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF = "urlclassifier.keyupdatetime.";
  1381.  
  1382. // Once a day (interval in seconds)
  1383. PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME = 24 * 60 * 60;
  1384.  
  1385. // These are the names the server will respond with in protocol4 format
  1386. PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME = "clientkey";
  1387. PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME = "wrappedkey";
  1388.  
  1389.  
  1390. /**
  1391.  * Called by a UrlCrypto to get the current K_C
  1392.  *
  1393.  * @returns Array of numbers making up the client key or null if we 
  1394.  *          have no key
  1395.  */
  1396. PROT_UrlCryptoKeyManager.prototype.getClientKeyArray = function() {
  1397.   return this.clientKeyArray_;
  1398. }
  1399.  
  1400. /**
  1401.  * Called by a UrlCrypto to get WrappedKey
  1402.  *
  1403.  * @returns Opaque base64-encoded WrappedKey or null if we haven't
  1404.  *          gotten one
  1405.  */
  1406. PROT_UrlCryptoKeyManager.prototype.getWrappedKey = function() {
  1407.   return this.wrappedKey_;
  1408. }
  1409.  
  1410. /**
  1411.  * Change the key url.  When we do this, we go ahead and rekey.
  1412.  * @param keyUrl String
  1413.  */
  1414. PROT_UrlCryptoKeyManager.prototype.setKeyUrl = function(keyUrl) {
  1415.   // If it's the same key url, do nothing.
  1416.   if (keyUrl == this.keyUrl_)
  1417.     return;
  1418.  
  1419.   this.keyUrl_ = keyUrl;
  1420.   this.rekeyTries_ = 0;
  1421.  
  1422.   // Check to see if we should make a new getkey request.
  1423.   var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
  1424.   var nextRekey = prefs.getPref(this.getPrefName_(this.keyUrl_), 0);
  1425.   if (nextRekey < parseInt(Date.now() / 1000, 10)) {
  1426.     this.reKey();
  1427.   }
  1428. }
  1429.  
  1430. /**
  1431.  * Given a url, return the pref value to use (pref contains last update time).
  1432.  * We basically use the url up until query parameters.  This avoids duplicate
  1433.  * pref entries as version number changes over time.
  1434.  * @param url String getkey URL
  1435.  */
  1436. PROT_UrlCryptoKeyManager.prototype.getPrefName_ = function(url) {
  1437.   var queryParam = url.indexOf("?");
  1438.   if (queryParam != -1) {
  1439.     return url.substring(0, queryParam);
  1440.   }
  1441.   return url;
  1442. }
  1443.  
  1444. /**
  1445.  * Tell the manager to re-key. For safety, this method still obeys the
  1446.  * max-tries limit. Clients should generally use maybeReKey() if they
  1447.  * want to try a re-keying: it's an error to call reKey() after we've
  1448.  * hit max-tries, but not an error to call maybeReKey().
  1449.  */
  1450. PROT_UrlCryptoKeyManager.prototype.reKey = function() {
  1451.   
  1452.   if (this.rekeyTries_ > this.MAX_REKEY_TRIES)
  1453.     throw new Error("Have already rekeyed " + this.rekeyTries_ + " times");
  1454.  
  1455.   this.rekeyTries_++;
  1456.  
  1457.   G_Debug(this, "Attempting to re-key");
  1458.   // If the keyUrl isn't set, we don't do anything.
  1459.   if (!this.testing_ && this.keyUrl_) {
  1460.     (new PROT_XMLFetcher()).get(this.keyUrl_,
  1461.                                 BindToObject(this.onGetKeyResponse, this));
  1462.  
  1463.     // Calculate the next time we're allowed to re-key.
  1464.     var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
  1465.     var nextRekey = parseInt(Date.now() / 1000, 10)
  1466.                   + PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME;
  1467.     prefs.setPref(this.getPrefName_(this.keyUrl_), nextRekey);
  1468.   }
  1469. }
  1470.  
  1471. /**
  1472.  * Try to re-key if we haven't already hit our limit. It's OK to call
  1473.  * this method multiple times, even if we've already tried to rekey
  1474.  * more than the max. It will simply refuse to do so.
  1475.  *
  1476.  * @returns Boolean indicating if it actually issued a rekey request (that
  1477.  *          is, if we haven' already hit the max)
  1478.  */
  1479. PROT_UrlCryptoKeyManager.prototype.maybeReKey = function() {
  1480.   if (this.rekeyTries_ > this.MAX_REKEY_TRIES) {
  1481.     G_Debug(this, "Not re-keying; already at max");
  1482.     return false;
  1483.   }
  1484.  
  1485.   this.reKey();
  1486.   return true;
  1487. }
  1488.  
  1489. /**
  1490.  * @returns Boolean indicating if we have a key we can use 
  1491.  */
  1492. PROT_UrlCryptoKeyManager.prototype.hasKey_ = function() {
  1493.   return this.clientKey_ != null && this.wrappedKey_ != null;
  1494. }
  1495.  
  1496. /**
  1497.  * Set a new key and serialize it to disk.
  1498.  *
  1499.  * @param clientKey String containing the base64-encoded client key 
  1500.  *                  we wish to use
  1501.  *
  1502.  * @param wrappedKey String containing the opaque base64-encoded WrappedKey
  1503.  *                   the server gave us (i.e., K_C encrypted with K_S)
  1504.  */
  1505. PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey, 
  1506.                                                           wrappedKey) {
  1507.   if (this.clientKey_)
  1508.     G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey);
  1509.  
  1510.   this.clientKey_ = clientKey;
  1511.   this.clientKeyArray_ = Array.map(atob(clientKey),
  1512.                                    function(c) { return c.charCodeAt(0); });
  1513.   this.wrappedKey_ = wrappedKey;
  1514.  
  1515.   this.serializeKey_(this.clientKey_, this.wrappedKey_);
  1516. }
  1517.  
  1518. /**
  1519.  * Try to write the key to disk so we can fall back on it. Fail
  1520.  * silently if we cannot. The keys are serialized in protocol4 format.
  1521.  *
  1522.  * @returns Boolean indicating whether we succeeded in serializing
  1523.  */
  1524. PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
  1525.  
  1526.   var map = {};
  1527.   map[this.CLIENT_KEY_NAME] = this.clientKey_;
  1528.   map[this.WRAPPED_KEY_NAME] = this.wrappedKey_;
  1529.   
  1530.   try {  
  1531.  
  1532.     var keyfile = Cc["@mozilla.org/file/directory_service;1"]
  1533.                  .getService(Ci.nsIProperties)
  1534.                  .get("ProfD", Ci.nsILocalFile); /* profile directory */
  1535.     keyfile.append(this.keyFilename_);
  1536.     var data = (new G_Protocol4Parser()).serialize(map);
  1537.  
  1538.     try {
  1539.       var stream = Cc["@mozilla.org/network/file-output-stream;1"]
  1540.                    .createInstance(Ci.nsIFileOutputStream);
  1541.       stream.init(keyfile,
  1542.                   0x02 | 0x08 | 0x20 /* PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE */,
  1543.                   -1 /* default perms */, 0 /* no special behavior */);
  1544.       stream.write(data, data.length);
  1545.     } finally {
  1546.       stream.close();
  1547.     }
  1548.     return true;
  1549.  
  1550.   } catch(e) {
  1551.  
  1552.     G_Error(this, "Failed to serialize new key: " + e);
  1553.     return false;
  1554.  
  1555.   }
  1556. }
  1557.  
  1558. /**
  1559.  * Invoked when we've received a protocol4 response to our getkey
  1560.  * request. Try to parse it and set this key as the new one if we can.
  1561.  *
  1562.  *  @param responseText String containing the protocol4 getkey response
  1563.  */ 
  1564. PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
  1565.  
  1566.   var response = (new G_Protocol4Parser).parse(responseText);
  1567.   var clientKey = response[this.CLIENT_KEY_NAME];
  1568.   var wrappedKey = response[this.WRAPPED_KEY_NAME];
  1569.  
  1570.   if (response && clientKey && wrappedKey) {
  1571.     G_Debug(this, "Got new key from: " + responseText);
  1572.     this.replaceKey_(clientKey, wrappedKey);
  1573.   } else {
  1574.     G_Debug(this, "Not a valid response for /getkey");
  1575.   }
  1576. }
  1577.  
  1578. /**
  1579.  * Attempt to read a key we've previously serialized from disk, so
  1580.  * that we can fall back on it in case we can't get one from the
  1581.  * server. If we get a key, only use it if we don't already have one
  1582.  * (i.e., if our startup HTTPS request died or hasn't yet completed).
  1583.  *
  1584.  * This method should be invoked early, like when the user's profile
  1585.  * becomes available.
  1586.  */ 
  1587. PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
  1588.   
  1589.   var oldKey = null;
  1590.   try {  
  1591.     var keyfile = Cc["@mozilla.org/file/directory_service;1"]
  1592.                  .getService(Ci.nsIProperties)
  1593.                  .get("ProfD", Ci.nsILocalFile); /* profile directory */
  1594.     keyfile.append(this.keyFilename_);
  1595.     if (keyfile.exists()) {
  1596.       try {
  1597.         var fis = Cc["@mozilla.org/network/file-input-stream;1"]
  1598.                   .createInstance(Ci.nsIFileInputStream);
  1599.         fis.init(keyfile, 0x01 /* PR_RDONLY */, 0444, 0);
  1600.         var stream = Cc["@mozilla.org/scriptableinputstream;1"]
  1601.                      .createInstance(Ci.nsIScriptableInputStream);
  1602.         stream.init(fis);
  1603.         oldKey = stream.read(stream.available());
  1604.       } finally {
  1605.         if (stream)
  1606.           stream.close();
  1607.       }
  1608.     }
  1609.   } catch(e) {
  1610.     G_Debug(this, "Caught " + e + " trying to read keyfile");
  1611.     return;
  1612.   }
  1613.    
  1614.   if (!oldKey) {
  1615.     G_Debug(this, "Couldn't find old key.");
  1616.     return;
  1617.   }
  1618.  
  1619.   oldKey = (new G_Protocol4Parser).parse(oldKey);
  1620.   var clientKey = oldKey[this.CLIENT_KEY_NAME];
  1621.   var wrappedKey = oldKey[this.WRAPPED_KEY_NAME];
  1622.  
  1623.   if (oldKey && clientKey && wrappedKey && !this.hasKey_()) {
  1624.     G_Debug(this, "Read old key from disk.");
  1625.     this.replaceKey_(clientKey, wrappedKey);
  1626.   }
  1627. }
  1628.  
  1629.  
  1630. //@line 36 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\content\xml-fetcher.js"
  1631.  
  1632. // A simple class that encapsulates a request. You'll notice the
  1633. // style here is different from the rest of the extension; that's
  1634. // because this was re-used from really old code we had. At some
  1635. // point it might be nice to replace this with something better
  1636. // (e.g., something that has explicit onerror handler, ability
  1637. // to set headers, and so on).
  1638. //
  1639. // The only interesting thing here is its ability to strip cookies
  1640. // from the request.
  1641.  
  1642. /**
  1643.  * Because we might be in a component, we can't just assume that
  1644.  * XMLHttpRequest exists. So we use this tiny factory function to wrap the
  1645.  * XPCOM version.
  1646.  *
  1647.  * @return XMLHttpRequest object
  1648.  */
  1649. function PROT_NewXMLHttpRequest() {
  1650.   var Cc = Components.classes;
  1651.   var Ci = Components.interfaces;
  1652.   var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
  1653.                 .createInstance(Ci.nsIXMLHttpRequest);
  1654.   // Need the following so we get onerror/load/progresschange
  1655.   request.QueryInterface(Ci.nsIJSXMLHttpRequest);
  1656.   return request;
  1657. }
  1658.  
  1659. /**
  1660.  * A helper class that does HTTP GETs and calls back a function with
  1661.  * the content it receives. Asynchronous, so uses a closure for the
  1662.  * callback.
  1663.  *
  1664.  * @param opt_stripCookies Boolean indicating whether we should strip
  1665.  *                         cookies from this request
  1666.  * 
  1667.  * @constructor
  1668.  */
  1669. function PROT_XMLFetcher(opt_stripCookies) {
  1670.   this.debugZone = "xmlfetcher";
  1671.   this._request = PROT_NewXMLHttpRequest();
  1672.   this._stripCookies = !!opt_stripCookies;
  1673. }
  1674.  
  1675. PROT_XMLFetcher.prototype = {
  1676.   /**
  1677.    * Function that will be called back upon fetch completion.
  1678.    */
  1679.   _callback: null,
  1680.   
  1681.  
  1682.   /**
  1683.    * Fetches some content.
  1684.    * 
  1685.    * @param page URL to fetch
  1686.    * @param callback Function to call back when complete.
  1687.    */
  1688.   get: function(page, callback) {
  1689.     this._request.abort();                // abort() is asynchronous, so
  1690.     this._request = PROT_NewXMLHttpRequest();
  1691.     this._callback = callback;
  1692.     var asynchronous = true;
  1693.     this._request.open("GET", page, asynchronous);
  1694.  
  1695.     if (this._stripCookies)
  1696.       new PROT_CookieStripper(this._request.channel);
  1697.  
  1698.     // Create a closure
  1699.     var self = this;
  1700.     this._request.onreadystatechange = function() {
  1701.       self.readyStateChange(self);
  1702.     }
  1703.  
  1704.     this._request.send(null);
  1705.   },
  1706.  
  1707.   /**
  1708.    * Called periodically by the request to indicate some state change. 4
  1709.    * means content has been received.
  1710.    */
  1711.   readyStateChange: function(fetcher) {
  1712.     if (fetcher._request.readyState != 4)
  1713.       return;
  1714.  
  1715.     // If the request fails, on trunk we get status set to
  1716.     // NS_ERROR_NOT_AVAILABLE.  On 1.8.1 branch we get an exception
  1717.     // forwarded from nsIHttpChannel::GetResponseStatus.  To be consistent
  1718.     // between branch and trunk, we send back NS_ERROR_NOT_AVAILABLE for
  1719.     // http failures.
  1720.     var responseText = null;
  1721.     var status = Components.results.NS_ERROR_NOT_AVAILABLE;
  1722.     try {
  1723.       G_Debug(this, "xml fetch status code: \"" + 
  1724.               fetcher._request.status + "\"");
  1725.       status = fetcher._request.status;
  1726.       responseText = fetcher._request.responseText;
  1727.     } catch(e) {
  1728.       G_Debug(this, "Caught exception trying to read xmlhttprequest " +
  1729.               "status/response.");
  1730.       G_Debug(this, e);
  1731.     }
  1732.     if (fetcher._callback)
  1733.       fetcher._callback(responseText, status);
  1734.   }
  1735. };
  1736.  
  1737.  
  1738. /**
  1739.  * This class knows how to strip cookies from an HTTP request. It
  1740.  * listens for http-on-modify-request, and modifies the request
  1741.  * accordingly. We can't do this using xmlhttprequest.setHeader() or
  1742.  * nsIChannel.setRequestHeader() before send()ing because the cookie
  1743.  * service is called after send().
  1744.  * 
  1745.  * @param channel nsIChannel in which the request is happening
  1746.  * @constructor
  1747.  */
  1748. function PROT_CookieStripper(channel) {
  1749.   this.debugZone = "cookiestripper";
  1750.   this.topic_ = "http-on-modify-request";
  1751.   this.channel_ = channel;
  1752.  
  1753.   var Cc = Components.classes;
  1754.   var Ci = Components.interfaces;
  1755.   this.observerService_ = Cc["@mozilla.org/observer-service;1"]
  1756.                           .getService(Ci.nsIObserverService);
  1757.   this.observerService_.addObserver(this, this.topic_, false);
  1758.  
  1759.   // If the request doesn't issue, don't hang around forever
  1760.   var twentySeconds = 20 * 1000;
  1761.   this.alarm_ = new G_Alarm(BindToObject(this.stopObserving, this), 
  1762.                             twentySeconds);
  1763. }
  1764.  
  1765. /**
  1766.  * Invoked by the observerservice. See nsIObserve.
  1767.  */
  1768. PROT_CookieStripper.prototype.observe = function(subject, topic, data) {
  1769.   if (topic != this.topic_ || subject != this.channel_)
  1770.     return;
  1771.  
  1772.   G_Debug(this, "Stripping cookies for channel.");
  1773.  
  1774.   this.channel_.QueryInterface(Components.interfaces.nsIHttpChannel);
  1775.   this.channel_.setRequestHeader("Cookie", "", false /* replace, not add */);
  1776.   this.alarm_.cancel();
  1777.   this.stopObserving();
  1778. }
  1779.  
  1780. /**
  1781.  * Remove us from the observerservice
  1782.  */
  1783. PROT_CookieStripper.prototype.stopObserving = function() {
  1784.   G_Debug(this, "Removing observer");
  1785.   this.observerService_.removeObserver(this, this.topic_);
  1786.   this.channel_ = this.alarm_ = this.observerService_ = null;
  1787. }
  1788.  
  1789. /**
  1790.  * XPCOM cruft
  1791.  */
  1792. PROT_CookieStripper.prototype.QueryInterface = function(iid) {
  1793.   var Ci = Components.interfaces;
  1794.   if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserve))
  1795.     return this;
  1796.   throw Components.results.NS_ERROR_NO_INTERFACE;
  1797. }
  1798.  
  1799. //@line 58 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\toolkit\components\url-classifier\src\nsUrlClassifierLib.js"
  1800.  
  1801. // Expose this whole component.
  1802. var lib = this;
  1803.  
  1804. function UrlClassifierLib() {
  1805.   this.wrappedJSObject = lib;
  1806. }
  1807.  
  1808. // Module object
  1809. function UrlClassifierLibMod() {
  1810.   this.firstTime = true;
  1811.   this.cid = Components.ID("{26a4a019-2827-4a89-a85c-5931a678823a}");
  1812.   this.progid = "@mozilla.org/url-classifier/jslib;1";
  1813. }
  1814.  
  1815. UrlClassifierLibMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
  1816.   if (this.firstTime) {
  1817.     this.firstTime = false;
  1818.     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  1819.   }
  1820.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  1821.   compMgr.registerFactoryLocation(this.cid,
  1822.                                   "UrlClassifier JS Lib",
  1823.                                   this.progid,
  1824.                                   fileSpec,
  1825.                                   loc,
  1826.                                   type);
  1827. };
  1828.  
  1829. UrlClassifierLibMod.prototype.getClassObject = function(compMgr, cid, iid) {  
  1830.   if (!cid.equals(this.cid))
  1831.     throw Components.results.NS_ERROR_NO_INTERFACE;
  1832.   if (!iid.equals(Ci.nsIFactory))
  1833.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1834.  
  1835.   return this.factory;
  1836. }
  1837.  
  1838. UrlClassifierLibMod.prototype.canUnload = function(compMgr) {
  1839.   return true;
  1840. }
  1841.  
  1842. UrlClassifierLibMod.prototype.factory = {
  1843.   createInstance: function(outer, iid) {
  1844.     if (outer != null)
  1845.       throw Components.results.NS_ERROR_NO_AGGREGATION;
  1846.     return new UrlClassifierLib();
  1847.   }
  1848. };
  1849.  
  1850. var LibModInst = new UrlClassifierLibMod();
  1851.  
  1852. function NSGetModule(compMgr, fileSpec) {
  1853.   return LibModInst;
  1854. }
  1855.